package vn.mahjonggame.metadata;

import java.util.Collections;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Timer;
import java.util.concurrent.ConcurrentHashMap;

import org.apache.log4j.Logger;

import vn.mahjonggame.game.Mahjong;
import vn.mahjonggame.platform.GameplayConfig;
import vn.mahjonggame.platform.PlatformConfig;
import vn.mahjonggame.util.Common;
import vn.mahjonggame.util.TileComparator;

public class Player implements Comparable<Player> {
	
	private int playerId;
	private int playerPosition;
	private String name;
	private Mahjong mahjong;
	
	private String playerState;
	
	private boolean isDisconnect;
	private boolean isContributeToDeathWall;
	private boolean isPointing;
	private boolean isJustThrown;
	
	private boolean isTempFuriten;
	private boolean isForeverFuriten;
	private boolean isRiichiWaiting;
	
	private boolean isRon;
	private boolean isTsumo;
	private boolean isInconclusive;
	
	private boolean isPublic;
	
	private int numTimesCallKan;	
	
	private boolean isRyanmen;
	private boolean isKanchan;
	private boolean isPenchan;
	private boolean isShanpon;
	private boolean isTanki;
	
	private boolean hasPicked;
	private boolean hasThrown;
	
	private Tile justPickedTile;
	private Tile justThrownTile;
	private int numYaku;
	private int numYakuMan;
		
	private boolean isKan;
	private boolean isPon;
	private boolean isChi;
	private boolean isLateKan;
	
	/*
	 * YAKU
	 */	
	private boolean isMenzenTsumo;
	private boolean isTenpai;
	private boolean isRiichi;	
	private boolean isIppatsu; //This yaku should be verify on Mahjong, need to check all players
	private boolean isTanyao;
	private boolean isPinfu;
	private boolean isFanpai;
	private boolean isRinshanKaihou;
	private boolean isChankan;
	private boolean isHaiteiRaoyue;
	private boolean isHaiteiRaoyui;
	private boolean isIipeikou;
	private boolean isSanshokuShoudoukou;	
	private boolean isSanRenkou;
	private boolean isDoubleRiichi;
	private boolean isSanshokuDoujin;
	private boolean isSanshokuDoukou;
	private boolean isIttsuu;
	private boolean isChanta;
	private boolean isHonroutou;
	private boolean isToitoi;
	private boolean isSanAnkou;
	private boolean isSanKantsu;
	private boolean isChiiToitsu;
	private boolean isShouSangen;
	private boolean isHonichi;
	private boolean isJunchan;
	private boolean isRyanpeikou;
	private boolean isChinitsu;
	private boolean isNagashiMangan;
	
	/*
	 * YAKUMAN
	 */
	private boolean isKokushiMusou26Han;
	private boolean isKokushiMusou13Han;
	private boolean isSuuAnkou26Han;
	private boolean isSuuAnkou13Han;
	private boolean isDaiSangen13Han;
	private boolean isShouSuushii13Han;
	private boolean isDaiSuushii26Han;
	private boolean isRyuuIisou13Han;
	private boolean isTsuuIisou13Han;
	private boolean isDaiChisei26Han;
	private boolean isChinroutou13Han;
	private boolean isChuurenPoutou13Han;
	private boolean isChuurenPoutou26Han;
	private boolean isDaiSharin13Han;
	private boolean isSuuRenkou13Han;
	private boolean isSuuKantsu13Han;
	
	/*
	 * OTHER YAKUMAN
	 */
	private boolean isShiisanpuutaa;
	private boolean isShiisuupuutaa;
	
	private List<String> yakuList = null;
	private List<String> yakumanList = null;
	private List<String> otherYakumanList = null;
	
	/*
	 * HAN Score
	 */
	private int hanScore = 0;
	
	/*
	 * FU Score
	 */
	private int fuScore = 0;
	
	/*
	 * Basic Score
	 */
	private double score = 0;
	
	/*
	 * Final Score
	 */
	private double finalScore = 0;
	
	/*
	 * Extended Score
	 */
	private double extendedScore = 0;
	
	/*
	 * List of the tile on player wall (2 layer)
	 */
	private List<List<Tile>> wall = null;
	/*
	 * List of the tile on the left hand (1 layer) 
	 */
	private List<Tile> leftTiles = null;
	/*
	 * List of the tile on the right hand (1 layer)
	 */
	private List<Tile> rightTiles = null;
	
	/*
	 * List of the tile that was thrown
	 */
	private List<Tile> thrownTiles = null;
	
	/*
	 * List of the tile that don't contribute to any Tile Set or Tile Pair
	 */
	private List<Tile> discretedTiles = null;
	
	private List<Integer> ponTileIndexs = null;
	
	/*
	 * KAN SET
	 */
	private List<Tile> kanSet = null;
	
	/*
	 * PON SET
	 */
	private List<Tile> ponSet = null;
	
	/*
	 * CHI SET
	 */
	private List<List<Tile>> chiSet = null;
	
	/*
	 * PAIR SET
	 */
	private List<Tile> pairSet = null;
	
	/*
	 * CALLED CHI SET
	 */
	private List<Tile> calledChiSet = null;
	
	/*
	 * CALLED PON SET
	 */
	private List<Tile> calledPonSet = null;
	
	/*
	 * CALLED KAN SET
	 */
	private List<Tile> calledKanSet = null;
	
	/*
	 * Start/End Death Wall
	 */
	private int startDeathWall = -1;
	private int endDeathWall = -1;
	
	/*
	 * Start/End Dora List
	 */
	private int startDoras = -1;
	private int endDoras = -1;
	
	/*
	 * List of the tile which was picked from death wall
	 */
	private List<Tile> pickedDeathTile = new ArrayList<Tile>();
	
	/*
	 * Number of kokushiMusou tile
	 */
	private int numKokushiMusou = 0;
	
	/*
	 * List of tile that is not in Yaochuhai
	 */
	private List<Tile> nonYaochuhaiTiles = new ArrayList<Tile>();
	
	private int turnTime;
	private int bonusTime;
	private long startTime;
	private long endTime;
	
	private Logger logger = null;
	
	private Map cache = new ConcurrentHashMap();
	private Timer timer = null;
	private List<TileSet> skipTypes = new ArrayList<TileSet>();
	private boolean isConflict = false;
	private List<Tile> willPickTiles = new ArrayList<Tile>();
	
	public Player(int playerId, int playerPosition, Mahjong mahjong){
		this.setPlayerId(playerId);
		this.setPlayerPosition(playerPosition);
		this.mahjong = mahjong;
		
		/*
		 * The below set is updated during the playing time
		 */
		this.setWall(new ArrayList<List<Tile>>());
		this.setLeftTiles(new ArrayList<Tile>());
		this.setRightTiles(new ArrayList<Tile>());
		this.setThrownTiles(new ArrayList<Tile>());		
		this.setDiscretedTiles(new ArrayList<Tile>());
		this.setPonTileIndexs(new ArrayList<Integer>());
		
		this.setCalledKanSet(new ArrayList<Tile>());
		this.setCalledPonSet(new ArrayList<Tile>());
		this.setCalledChiSet(new ArrayList<Tile>());
		
		this.setJustPickedTile(null);
		
		/*
		 * The below set is updated in end match
		 */
		this.setKanSet(new ArrayList<Tile>());
		this.setPonSet(new ArrayList<Tile>());
		this.setChiSet(new ArrayList<List<Tile>>());
		this.setPairSet(new ArrayList<Tile>());
		
		this.setYakuList(new ArrayList<String>());
		this.setYakumanList(new ArrayList<String>());
		this.setOtherYakumanList(new ArrayList<String>());
		
		this.setPlayerState(PlayerState.NOSTATE.toString());		
	}

	public int getPlayerId() {
		return playerId;
	}

	public void setPlayerId(int playerId) {
		this.playerId = playerId;
	}

	@Override
	public int compareTo(Player player) {
		// TODO Auto-generated method stub		
		return this.getPlayerId() - player.getPlayerId();
	}
	
	@Override
	public String toString(){
		return String.valueOf(this.getName() + "(" + this.getPlayerId()) + ") - " + String.valueOf(this.getPlayerPosition());
	}
	
	public synchronized boolean isBadTileSet(){		
		Set<Integer> tileIds = new HashSet<Integer>();
		
		List<Tile> leftTile = new ArrayList<Tile>();
		leftTile.addAll(this.getLeftTiles());				
		
		for (Iterator<Tile> iTiles = leftTile.iterator(); iTiles.hasNext(); ){
			Tile iTile = iTiles.next();
			if (iTile.isYaochuhai()){
				tileIds.add(iTile.getId());
			}				
		}
		
		if(tileIds.size() >= 9){
			return true;
		}
		
		return false;
	}
	
	/*
	 * +n HAN
	 */
	public void addHanScore(int n){
		int han = this.getHanScore();
		han = han + n;
		this.setHanScore(han);
	}
	
	/*
	 * YAKU
	 */
	
	/*
	 * Tenpai
	 */
	public void tenpai(){
		
		logger.info("Cheking tenpai...");
		logger.info("kanSet: " + kanSet);
		logger.info("ponSet: " + ponSet);
		logger.info("chiSet: " + chiSet);
		logger.info("pairSet: " + pairSet);
		logger.info("discretedTiles: " + discretedTiles);
		
		int kanSetSize = kanSet.size();
		int ponSetSize = ponSet.size();
		int chiSetSize = chiSet.size();
		int pairSetSize = pairSet.size();
		int discretedTilesSize = discretedTiles.size();
		
		int numSet = kanSetSize + ponSetSize + chiSetSize;
		
		if(discretedTilesSize > 0){
			if((numSet == 4 && pairSetSize == 0)
					|| (numSet == 3 && pairSetSize == 2)
					|| (pairSetSize == 6)
					|| this.getNumKokushiMusou() == 12){
				this.setTenpai(true);
			}
			else if(numSet == 3 && pairSetSize == 1){
				int index1 = this.getDiscretedTiles().get(0).getIndex();
				int index2 = this.getDiscretedTiles().get(1).getIndex();
				int alt = Math.abs(index1 - index2);
				if(alt > 0 && alt < 3 ){
					this.setTenpai(true);
				}
			}
		}		
				
	}	
	
	/*
	 * Menzen Tsumo
	 */
	public void menzenTsumo(){
		this.setMenzenTsumo(!this.isPublic());		
	}
	
	/*
	 * Riichi
	 */
	public void riichi(){
		this.setRiichi(!this.isPublic() && this.isTenpai());
	}
	
	/*
	 * Tanyao
	 */
	public void tanyao(){
		this.setTanyao(true);
		
		List<Tile> kanSet = this.getKanSet();
		List<Tile> ponSet = this.getPonSet();
		List<List<Tile>> chiSet = this.getChiSet();
		List<Tile> pairSet = this.getPairSet();
		
		for(Iterator<Tile> iTiles = kanSet.iterator(); iTiles.hasNext();){
			Tile iTile = iTiles.next();
			
			if(iTile.isYaochuhai()){
				this.setTanyao(false);
				return;
			}
		}
		
		for(Iterator<Tile> iTiles = ponSet.iterator(); iTiles.hasNext();){
			Tile iTile = iTiles.next();
			
			if(iTile.isYaochuhai()){
				this.setTanyao(false);
				return;
			}
		}
		
		for(Iterator<List<Tile>> iTiless = chiSet.iterator(); iTiless.hasNext();){
			List<Tile> iTiles = iTiless.next();
			for(Iterator<Tile> tiles = iTiles.iterator(); tiles.hasNext();){
				Tile iTile = tiles.next();
				if(iTile.isYaochuhai()){
					this.setTanyao(false);
					return;
				}
			}			
		}
		
		for(Iterator<Tile> iTiles = pairSet.iterator(); iTiles.hasNext();){
			Tile iTile = iTiles.next();
			
			if(iTile.isYaochuhai()){
				this.setTanyao(false);
				return;
			}
		}

	}
	
	/*
	 * Pinfu
	 */
	public void pinfu(){
		this.setPinfu(true);
		
		List<List<Tile>> chiSet = this.getChiSet();
		
		if(this.isPublic() || chiSet.size() != 4){
			this.setPinfu(false);			
		}			
	}
	
	/*
	 * Fanpai / Yakuhai
	 */
	public void fanpai(){
		List<Tile> ponSet = this.getPonSet();
		
		for(Iterator<Tile> iTiles = ponSet.iterator(); iTiles.hasNext();){
			Tile iTile = iTiles.next();
			if(iTile.isJihai()){
				this.setFanpai(true);
				return;
			}
		}
	}
	
	/*
	 * Rinshan Kaihou
	 * TODO Check this Yaku in pickTileFromDeathWall
	 */
	public void rinshanKaihou(){
		this.setRinshanKaihou(this.isTenpai());
	}
	
	/*
	 * Chankan
	 */
	public void chankan(){
		this.setChankan(this.isTenpai());
	}
	
	/*
	 * Haitei Raoyue
	 */
	public void haiteiRaoyue(){
		this.setHaiteiRaoyue(this.isTenpai());
	}	
	
	/*
	 * Haitei Raoyui
	 */
	public void haiteiRaoyui(){
		this.setHaiteiRaoyui(this.isTenpai());
	}
	
	/*
	 * Iipeikou
	 */
	public void iipeikou(){
		
		List<List<Tile>> chiSet = this.getChiSet();
		
		int counter = 1;
		TileComparator c = new TileComparator();
		
		for(int i = 0; i < chiSet.size() - 1; i++){
			List<Tile> tiles = chiSet.get(i);
			List<Tile> nextTiles = chiSet.get(i + 1);
			Collections.sort(tiles, c);
			Collections.sort(nextTiles, c);
			Tile tile = tiles.get(0);
			Tile nextTile = nextTiles.get(0);
			
			if(tile.getType() == nextTile.getType() && tile.getIndex() == nextTile.getIndex()){
				counter++;
			}			
		}
		
		if(counter == 2 && !this.isPublic()){
			this.setIipeikou(true);
		}
		
	}
	
	/*
	 * Sanshoku Shoudoukou
	 */
	public void sanshokuShoudoukou(){
		List<Tile> ponSet = this.getPonSet();
		List<Tile> pairSet = this.getPairSet();
		
		Tile pairTile = pairSet.get(0);
		int counter = 0;
		
		for(Iterator<Tile> iTiles = ponSet.iterator(); iTiles.hasNext();){
			Tile tile = iTiles.next();
						
			if(tile.getType() != pairTile.getType() && tile.getIndex() == pairTile.getIndex()){
				counter++;
			}
		}
		
		if(counter == 2){
			this.setSanshokuShoudoukou(true);
		}
	}
	
	/* 
	 * San Renkou
	 */
	public void sanRenkou() {
		
		List<Tile> ponSet = this.getPonSet();
		
		if(ponSet.size() == 3){
			int firstIndex = ponSet.get(0).getIndex();
			int secondIndex = ponSet.get(1).getIndex();			
			int thirdIndex = ponSet.get(2).getIndex();
			
			if(firstIndex == secondIndex - 1 && secondIndex == thirdIndex - 1){
				this.setSanRenkou(true);
			}
		}
	}
	
	/*
	 * Sanshoku Doujin
	 */
	public void sanshokuDoujin(){
		List<List<Tile>> chiSet = this.getChiSet();
		
		if(chiSet.size() == 3){
			Tile tile0 = chiSet.get(0).get(0);
			Tile tile1 = chiSet.get(1).get(0);
			Tile tile2 = chiSet.get(2).get(0);
			
			int index0 = tile0.getIndex();
			int index1 = tile1.getIndex();
			int index2 = tile2.getIndex();
			
			TileType type0 = tile0.getType();
			TileType type1 = tile1.getType();
			TileType type2 = tile2.getType();
			
			if(index0 == index1 && index1 == index2){
				if(type0 != type1 && type1 != type2 && type0 != type2){
					this.setSanshokuDoujin(true);
				}
			}
		}		
	}
	
	/*
	 * Sanshoku Doukou
	 */
	public void sanshokuDoukou(){
		List<Tile> ponSet = this.getPonSet();
		
		if(ponSet.size() == 3){
			Tile tile0 = ponSet.get(0);
			Tile tile1 = ponSet.get(1);
			Tile tile2 = ponSet.get(2);
			
			int index0 = tile0.getIndex();
			int index1 = tile1.getIndex();
			int index2 = tile2.getIndex();
			
			TileType type0 = tile0.getType();
			TileType type1 = tile1.getType();
			TileType type2 = tile2.getType();
			
			if(index0 == index1 && index1 == index2){
				if(type0 != type1 && type1 != type2 && type0 != type2){
					this.setSanshokuDoujin(true);
				}
			}
		}
	}
	
	/*
	 * Ittsuu
	 */
	public void ittsuu(){
		List<List<Tile>> chiSet = this.getChiSet();
		
		if(chiSet.size() == 3){
			Tile tile0 = chiSet.get(0).get(0);
			Tile tile1 = chiSet.get(1).get(0);
			Tile tile2 = chiSet.get(2).get(0);
			
			int index0 = tile0.getIndex();
			int index1 = tile1.getIndex();
			int index2 = tile2.getIndex();
			
			TileType type0 = tile0.getType();
			TileType type1 = tile1.getType();
			TileType type2 = tile2.getType();
			
			if(type0 == type1 && type1 == type2){
				if(index1 - index0 == 3 && index2 - index1 == 3){
					this.setIttsuu(true);
				}
			}			
		}
	}
	
	/*
	 * Chanta
	 */
	public void chanta(){		
		
		List<Tile> kanSet = this.getKanSet();
		List<Tile> ponSet = this.getPonSet();
		List<List<Tile>> chiSet = this.getChiSet();
		List<Tile> pairSet = this.getPairSet();
				
		for(int i = 0; i < kanSet.size(); i++){
			Tile tile = kanSet.get(i);
			if(!tile.isYaochuhai()){
				this.setChanta(false);
				break;
			}
		}
		
		for(int i = 0; i < ponSet.size(); i++){
			Tile tile = ponSet.get(i);
			
			if(!tile.isYaochuhai()){
				this.setChanta(false);
				break;
			}
		}
		
		for(int i = 0; i < chiSet.size(); i++){
			List<Tile> tiles = chiSet.get(i);
			int counter = 0;
			for(int j = 0; j < tiles.size(); j++){
				Tile tile = tiles.get(j);				
				if(tile.isYaochuhai()){
					counter++;
				}
			}
			
			if(counter == 0){
				this.setChanta(false);
				break;
			}
		}
		
		for(int i = 0; i < pairSet.size(); i++){
			Tile tile = pairSet.get(i);
			if(!tile.isYaochuhai()){
				this.setChanta(false);
				break;
			}			
		}
		
		this.setChanta(true);
	}
	
	/*
	 * Honroutou
	 */
	public void honroutou(){
		List<Tile> kanSet = this.getKanSet();
		List<Tile> ponSet = this.getPonSet();
		List<List<Tile>> chiSet = this.getChiSet();
		List<Tile> pairSet = this.getPairSet();
		
		for(int i = 0; i < kanSet.size(); i++){
			Tile tile = kanSet.get(i);
			if(!tile.isYaochuhai()){
				this.setChanta(false);
				return;
			}
		}
		
		for(int i = 0; i < ponSet.size(); i++){
			Tile tile = ponSet.get(i);
			
			if(!tile.isYaochuhai()){
				this.setChanta(false);
				return;
			}
		}
		
		for(int i = 0; i < chiSet.size(); i++){
			List<Tile> tiles = chiSet.get(i);			
			for(int j = 0; j < tiles.size(); j++){
				Tile tile = tiles.get(j);				
				if(!tile.isYaochuhai()){
					this.setChanta(false);
					return;
				}
			}			
		}
		
		for(int i = 0; i < pairSet.size(); i++){
			Tile tile = pairSet.get(i);
			if(!tile.isYaochuhai()){
				this.setChanta(false);
				return;
			}			
		}
		
		
		this.setHonroutou(true);
	}
	
	/*
	 * Toitoi
	 */
	public void toitoi(){
		if(this.getPonSet().size() == 4){
			this.setToitoi(true);
		}
	}
	
	/*
	 * San Ankou
	 */
	public void sanAnkou(){
		List<Tile> ponSet = this.getPonSet();
		int ponSetSize = ponSet.size();
		
		boolean isClose = true;
		for(int i = 0; i < ponSetSize; i++){
			Tile tile = ponSet.get(i);
			if(this.getRightTiles().contains(tile)){
				isClose = false;
				break;
			}
		}
		
		if(ponSetSize == 3 && isClose){
			this.setSanAnkou(true);
		}
	}
	
	/*
	 * San Kanstu
	 */
	public void sanKanstu(){
		if(this.getKanSet().size() == 3){
			this.setSanKantsu(true);
		}
	}
	
	/*
	 * Chiitoitsu
	 */
	public void chiitoisu(){
		if(this.getPairSet().size() == 7){
			this.setChiiToitsu(true);
		}
	}
	
	/*
	 * Shou Shangen
	 */
	public void shouShangen(){
		int rongPair = 0;
		int rongSet = 0;
		
		List<Tile> kanSet = this.getKanSet();
		List<Tile> ponSet = this.getPonSet();
		List<Tile> pairSet = this.getPairSet();
		
		for(int i = 0; i < kanSet.size(); i++){
			Tile tile = kanSet.get(i);
			if(tile.isJihai()){
				rongSet++;
			}
		}
		
		for(int i = 0; i < ponSet.size(); i++){
			Tile tile = ponSet.get(i);
			if(tile.isJihai()){
				rongSet++;
			}
		}
		
		for(int i = 0; i < pairSet.size(); i++){
			Tile tile = pairSet.get(i);
			if(tile.isJihai()){
				rongPair++;
			}
		}
		
		if(rongSet == 2 && rongPair == 1){
			this.setShouSangen(true);
		}
	}
	
	/*
	 * Honitsu / Honichi
	 */
	public void honichi(){
		List<Tile> leftTile = this.getLeftTiles();
		Tile tile = leftTile.get(0);
		
		boolean flag = true;
		
		for(int i = 1; i < leftTile.size(); i++){
			Tile tileTmp = leftTile.get(i);
			if(tileTmp.getType() == tile.getType()){
				continue;
			}
			else if (tileTmp.isJihai()){
				continue;
			}
			else {
				flag = false;
				break;
			}
		}
		
		this.setHonichi(flag);
	}
	/*
	 * Junchan
	 */
	public void junchan(){
		List<Tile> kanSet = this.getKanSet();
		List<Tile> ponSet = this.getPonSet();
		List<List<Tile>> chiSet = this.getChiSet();
		List<Tile> pairSet = this.getPairSet();
		
		this.setJunchan(true);
		
		for(int i = 0; i < kanSet.size(); i++){
			Tile tile = kanSet.get(i);
			if(!tile.isYaochuhai() || tile.isJihai()){
				this.setJunchan(false);
				break;
			}
		}
		
		for(int i = 0; i < ponSet.size(); i++){
			Tile tile = ponSet.get(i);
			
			if(!tile.isYaochuhai() || tile.isJihai()){
				this.setJunchan(false);
				break;
			}
		}
		
		for(int i = 0; i < chiSet.size(); i++){
			List<Tile> tiles = chiSet.get(i);
			int counter = 0;
			for(int j = 0; j < tiles.size(); j++){
				Tile tile = tiles.get(j);				
				if(tile.isYaochuhai() && !tile.isJihai()){
					counter++;
				}
			}
			
			if(counter == 0){
				this.setJunchan(false);
				break;
			}
		}
		
		for(int i = 0; i < pairSet.size(); i++){
			Tile tile = pairSet.get(i);
			if(!tile.isYaochuhai() || tile.isJihai()){
				this.setJunchan(false);
				break;
			}			
		}			
	}
	
	/*
	 * Ryanpeikou
	 */
	public void ryanpeikou(){
		List<Tile> tiles = new ArrayList<Tile>();
		tiles.addAll(this.getKanSet());
		tiles.addAll(this.getPonSet());
		
		TileComparator c = new TileComparator();
		
		Collections.sort(tiles, c);
		
		for(int i = 0; i < tiles.size() - 1; i++){
			Tile tile = tiles.get(i);
			Tile nextTile = tiles.get(i+1);
			
			if(tile.getType() == nextTile.getType() && (nextTile.getIndex() - tile.getIndex() == 1)){
				this.setRyanpeikou(true);
				
				return;
			}
		}
		
	}
	
	/*
	 * Chinitsu
	 */
	public void chinitsu(){		
		List<Tile> kanSet = this.getKanSet();
		List<Tile> ponSet = this.getPonSet();
		List<List<Tile>> chiSet = this.getChiSet();
		List<Tile> pairSet = this.getPairSet();
				
		Tile tile = this.getLeftTiles().get(0);
				
		if(!tile.isJihai()){
			for(int i = 0; i < kanSet.size(); i++){
				Tile tileTmp = kanSet.get(i);				
				if(tileTmp.isJihai() || tileTmp.getType() != tile.getType()){
					this.setChinitsu(false);
					return;
				}
			}
			
			for(int i = 0; i < ponSet.size(); i++){
				Tile tileTmp = ponSet.get(i);				
				if(tileTmp.isJihai() || tileTmp.getType() != tile.getType()){
					this.setChinitsu(false);
					return;
				}
			}
			
			for(int i = 0; i < chiSet.size(); i++){
				List<Tile> tilesTmp = chiSet.get(i);
				for(int j = 0; j < tilesTmp.size(); j++){
					Tile tileTmp = tilesTmp.get(j);
					
					if(tileTmp.isJihai() || tileTmp.getType() != tile.getType()){
						this.setChinitsu(false);
						return;
					}
				}				
			}
			
			for(int i = 0; i < pairSet.size(); i++){
				Tile tileTmp = pairSet.get(i);				
				if(tileTmp.isJihai() || tileTmp.getType() != tile.getType()){
					this.setChinitsu(false);
					return;
				}
			}
		}
		
		this.setChinitsu(true);
	}
	
	/*
	 * Nagashi Mangan
	 */
	public void nagashiMangan(){
		this.setNagashiMangan(true);
		
		if(!this.isInconclusive()
				|| this.getCalledKanSet().size() != 0
				|| this.getCalledPonSet().size() != 0
				|| this.getCalledChiSet().size() != 0){
			this.setNagashiMangan(false);
			return;
		}
		
		for(Iterator<Tile> iTiles = this.getThrownTiles().iterator(); iTiles.hasNext();){
			Tile tile = iTiles.next();
			
			if(tile.isYaochuhai()){
				this.setNagashiMangan(false);
				return;
			}
		}			
	}	
	
	/*
	 * YAKUMAN
	 */
	/*
	 * Kokushi Musou
	 */
	public void kokushiMusou(Tile tile){		
		List<Tile> tilesL = new ArrayList<Tile>();
		
		this.setNumKokushiMusou(0);
		this.getNonYaochuhaiTiles().clear();
		
		tilesL.addAll(this.getLeftTiles());
		tilesL.addAll(this.getRightTiles());
		
		Set<Tile> tilesS = new HashSet<Tile>();			
			
		if(!tile.isYaochuhai()){		
			return;
		}
		
		for(Iterator<Tile> iTiles = tilesL.iterator(); iTiles.hasNext();){
			Tile iTile = iTiles.next();
			
			if(iTile.isYaochuhai()){
				tilesS.add(iTile);
			}
			else {
				this.getNonYaochuhaiTiles().add(iTile);
			}
		}
		
		this.setNumKokushiMusou(tilesS.size());
		
		if(tilesS.size() == 13){
			this.setKokushiMusou26Han(true);
			this.setKokushiMusou13Han(false);
			return;
		}		
		else if(tilesS.size() == 12){
			this.setKokushiMusou26Han(false);
			this.setKokushiMusou13Han(true);
			return;
		}		
	}
	
	/*
	 * Suu Ankou / Suu Ankou Tanki
	 */
	public void suuAnkou(){
		List<Tile> tiles = new ArrayList<Tile>();
		List<Tile> ponSet = this.getPonSet();
		
		tiles.addAll(ponSet);
		
		tiles.retainAll(this.getRightTiles());
		
		if(tiles.size() == 0){
			if(ponSet.size() == 4){
				this.setSuuAnkou26Han(true);
				return;
			}
			else if(ponSet.size() == 3 && this.getPairSet().size() == 2){
				this.setSuuAnkou13Han(true);
				return;
			}
		}
	}
	
	/*
	 * Dai Sangen
	 */
	public void daiSangen(){
		int counter = 0;
		
		List<Tile> tiles = new ArrayList<Tile>();
		tiles.addAll(this.getPonSet());
		tiles.addAll(this.getKanSet());
		
		for(Iterator<Tile> iTiles = tiles.iterator(); iTiles.hasNext(); ){
			Tile tile = iTiles.next();
			
			if(tile.isRong()){
				counter++;
			}
		}
		
		if(counter == 3){
			this.setDaiSangen13Han(true);
		}
	}
	
	/*
	 * Shou Suushii
	 */
	public void shouSuushii(){
		int counter = 0;
		
		List<Tile> tiles = new ArrayList<Tile>();
		tiles.addAll(this.getPonSet());
		tiles.addAll(this.getKanSet());
		
		for(Iterator<Tile> iTiles = tiles.iterator(); iTiles.hasNext(); ){
			Tile tile = iTiles.next();
			
			if(tile.isGio()){
				counter++;
			}
		}
		
		Tile tile = this.getPairSet().get(0);
		
		if(counter == 3 && tile.isGio()){
			this.setShouSuushii13Han(true);
		}
	}
	
	/*
	 * Dai Suushii
	 */
	public void daiSuushii(){
		int counter = 0;
		
		List<Tile> tiles = new ArrayList<Tile>();
		tiles.addAll(this.getPonSet());
		tiles.addAll(this.getKanSet());
		
		for(Iterator<Tile> iTiles = tiles.iterator(); iTiles.hasNext(); ){
			Tile tile = iTiles.next();
			
			if(tile.isGio()){
				counter++;
			}
		}
		
		if(counter == 4){
			this.setDaiSuushii26Han(true);
		}
	}
	
	/*
	 * Ryuu Iisou
	 */
	public void ryuuIisou(){
		List<Tile> kanSet = this.getKanSet();
		List<Tile> ponSet = this.getPonSet();
		List<List<Tile>> chiSet = this.getChiSet();
		List<Tile> pairSet = this.getPairSet();			
		
		this.setRyuuIisou13Han(true);			
		
		for(Iterator<Tile> iTiles = kanSet.iterator(); iTiles.hasNext();){
			Tile tile = iTiles.next();
			if(!tile.isInRyuuIisou()){
				this.setRyuuIisou13Han(false);
				return;
			}			
		}
		
		for(Iterator<Tile> iTiles = ponSet.iterator(); iTiles.hasNext();){
			Tile tile = iTiles.next();
			if(!tile.isInRyuuIisou()){
				this.setRyuuIisou13Han(false);
				return;
			}			
		}
		
		for(Iterator<List<Tile>> iTiless = chiSet.iterator(); iTiless.hasNext();){
			List<Tile> iTiles = iTiless.next();
			for(Iterator<Tile> tiles = iTiles.iterator(); tiles.hasNext();){
				Tile iTile = tiles.next();
				
				if(!iTile.isInRyuuIisou()){
					this.setRyuuIisou13Han(false);
					return;
				}
			}					
		}
		
		for(Iterator<Tile> iTiles = pairSet.iterator(); iTiles.hasNext();){
			Tile tile = iTiles.next();
			if(!tile.isInRyuuIisou()){
				this.setRyuuIisou13Han(false);
				return;
			}			
		}
	}
	
	/*
	 * Tsuu Iisou
	 */
	public void tsuuIisou(){
		List<Tile> kanSet = this.getKanSet();
		List<Tile> ponSet = this.getPonSet();		
		List<Tile> pairSet = this.getPairSet();			
		
		this.setTsuuIisou13Han(true);			
		
		for(Iterator<Tile> iTiles = kanSet.iterator(); iTiles.hasNext();){
			Tile tile = iTiles.next();
			if(!tile.isJihai()){
				this.setTsuuIisou13Han(false);
				return;
			}			
		}
		
		for(Iterator<Tile> iTiles = ponSet.iterator(); iTiles.hasNext();){
			Tile tile = iTiles.next();
			if(!tile.isJihai()){
				this.setTsuuIisou13Han(false);
				return;
			}		
		}
	
		for(Iterator<Tile> iTiles = pairSet.iterator(); iTiles.hasNext();){
			Tile tile = iTiles.next();
			if(!tile.isJihai()){
				this.setTsuuIisou13Han(false);
				return;
			}		
		}
	}
	
	/*
	 * Dai Chisei
	 */
	public void daiChisei(){
		List<Tile> pairSet = this.getPairSet();
		
		if(pairSet.size() == 7){
			this.setDaiChisei26Han(true);
			for(Iterator<Tile> iTiles = pairSet.iterator(); iTiles.hasNext();){
				Tile tile = iTiles.next();
				if(!tile.isJihai()){
					this.setDaiChisei26Han(false);
					return;
				}		
			}
		}
	}
	
	/*
	 * Chinroutou
	 */
	public void chinroutou(){
		this.setChinroutou13Han(true);
		
		List<Tile> kanSet = this.getKanSet();
		List<Tile> ponSet = this.getPonSet();		
		List<Tile> pairSet = this.getPairSet();			
		
		for(Iterator<Tile> iTiles = kanSet.iterator(); iTiles.hasNext();){
			Tile tile = iTiles.next();
			if(!tile.is1Or9()){
				this.setChinroutou13Han(false);
				return;
			}			
		}
		
		for(Iterator<Tile> iTiles = ponSet.iterator(); iTiles.hasNext();){
			Tile tile = iTiles.next();
			if(!tile.is1Or9()){
				this.setChinroutou13Han(false);
				return;
			}		
		}
			
		for(Iterator<Tile> iTiles = pairSet.iterator(); iTiles.hasNext();){
			Tile tile = iTiles.next();
			if(!tile.is1Or9()){
				this.setChinroutou13Han(false);
				return;
			}		
		}
	}
	
	/*
	 * Chuuren Poutou
	 */
	public void chuurenPoutou(Tile tile){
		
		this.setChuurenPoutou13Han(true);
		this.setChuurenPoutou26Han(true);
				
		if(!tile.is1Or9()){
			this.setChuurenPoutou13Han(false);
			this.setChuurenPoutou26Han(false);
			return;
		}
		
		List<Tile> tiles = new ArrayList<Tile>();
		tiles.addAll(this.getLeftTiles());
		tiles.addAll(this.getRightTiles());
		
		for(Iterator<Tile> iTiles = tiles.iterator(); iTiles.hasNext();){
			Tile iTile = iTiles.next();			
			if(iTile.getType() != tile.getType()){
				this.setChuurenPoutou13Han(false);
				this.setChuurenPoutou26Han(false);
				return;
			}			
		}
		
		List<Tile> ponSet = this.getPonSet();
		
		for(Iterator<Tile> iTiles = ponSet.iterator(); iTiles.hasNext();){
			Tile iTile = iTiles.next();
			if(!iTile.is1Or9()){				
				this.setChuurenPoutou13Han(false);
				this.setChuurenPoutou26Han(false);
				return;
			}
			else {
				if(ponSet.contains(justPickedTile)){
					this.setChuurenPoutou13Han(true);
					this.setChuurenPoutou26Han(false);
					return;
				}
				else {
					this.setChuurenPoutou13Han(false);
					this.setChuurenPoutou26Han(true);
					return;
				}
			}
		}	
		
		List<Integer> tileIds = mahjong.tileArrayToTileIdArray(tiles);
		tileIds.removeAll(Collections.singleton(0));
		tileIds.removeAll(Collections.singleton(8));
		
		List<Integer> chuurenPoutouL = new ArrayList<Integer>();
		for(int i = 2; i < 8; i++) {
			chuurenPoutouL.add(i);		
		}
		
		chuurenPoutouL.retainAll(tileIds);
		
		if(chuurenPoutouL.size() == 6){
			this.setChuurenPoutou13Han(true);
			this.setChuurenPoutou26Han(false);
			return;
		}
		else if(chuurenPoutouL.size() == 7){
			this.setChuurenPoutou13Han(false);
			this.setChuurenPoutou26Han(true);
			return;
		}
		else {
			this.setChuurenPoutou13Han(false);
			this.setChuurenPoutou26Han(false);
			return;
		}
	}
	
	/*
	 * Dai Sharin (Pin) / Dai Chikurin (Sou) / Dai Suurin (Man) 	 
	 */
	public void daiSharin(Tile tile){
		this.setDaiSharin13Han(true);
				
		if(tile.is1Or9() || tile.isJihai()){
			this.setDaiSharin13Han(false);
			return;
		}
		
		List<Tile> tiles = new ArrayList<Tile>();
		tiles.addAll(this.getLeftTiles());
		tiles.addAll(this.getRightTiles());
		
		for(Iterator<Tile> iTiles = tiles.iterator(); iTiles.hasNext();){
			Tile iTile = iTiles.next();			
			if(iTile.getType() != tile.getType()){
				this.setDaiSharin13Han(false);
				return;
			}
		}
		
		String daiSharinStr = "22334455667788";
		String thisDaiSharinStr = mahjong.tileIdsIntToString(tiles); 
		
		if(daiSharinStr.equals(thisDaiSharinStr)){
			this.setDaiSharin13Han(true);			
		}
	}
	
	/*
	 * Suu Renkou
	 */
	public void suuRenkou(){
		List<Tile> ponSet = this.getPonSet();
		int ponSetSize = ponSet.size();
		boolean flag = false;
		
		for(int i = 0; i < ponSetSize - 1; i++){
			Tile iTile = ponSet.get(i);
			Tile iiTile = ponSet.get(i + 1);
			
			if(iTile.getType() == iiTile.getType() || (iTile.getIndex() != iiTile.getIndex() - 1)){
				flag = false;
				break;
			}
		}
		
		if(flag && ponSetSize == 4){
			this.setSuuRenkou13Han(true);
		}		
	}
	
	/*
	 * Suu Kantsu
	 */
	public void suuKantsu(){
		this.setSuuKantsu13Han(this.getKanSet().size() == 4);		
	}
	
	/*
	 * OTHER YAKUMAN
	 */
	public void shiisanpuutaa(){
		this.setShiisanpuutaa(true);
		
		List<Tile> kanSet = this.getKanSet();
		List<Tile> ponSet = this.getPonSet();
		List<List<Tile>> chiSet = this.getChiSet();
		List<Tile> pairSet = this.getPairSet();
		
		if(kanSet.size() > 0
				|| ponSet.size() > 0
				|| chiSet.size() > 0
				|| pairSet.size() != 1){
			this.setShiisanpuutaa(false);
			return;
		}
		
		List<Tile> tiles = new ArrayList<Tile>();
		tiles.addAll(this.getLeftTiles());
		tiles.addAll(this.getRightTiles());
		tiles.add(this.getJustPickedTile());
		
		tiles.removeAll(Collections.singleton(pairSet.get(0)));
		
		while(tiles.size() > 0){
			Tile tile = tiles.get(0);
			List<Tile> tilesTmp = mahjong.getTileByType(tile.getType().ordinal(), tiles);
			
			for(int i = 0; i < tilesTmp.size() - 1; i++){
				Tile tileTmp = tilesTmp.get(i);
				Tile nextTileTmp = tilesTmp.get(i + 1);
				
				if(nextTileTmp.getIndex() - tileTmp.getIndex() < 3){
					this.setShiisanpuutaa(false);
					return;
				}				
			}
			
			tiles.removeAll(tilesTmp);
		}
	}
	
	/*
	 * Shiisuupuutaa
	 */
	public void shiisuupuutaa(){
		this.setShiisuupuutaa(true);
		
		List<Tile> kanSet = this.getKanSet();
		List<Tile> ponSet = this.getPonSet();
		List<List<Tile>> chiSet = this.getChiSet();
		List<Tile> pairSet = this.getPairSet();
		
		if(kanSet.size() > 0
				|| ponSet.size() > 0
				|| chiSet.size() > 0
				|| pairSet.size() > 0){
			this.setShiisuupuutaa(false);
			return;
		}
		
		List<Tile> tiles = new ArrayList<Tile>();
		tiles.addAll(this.getLeftTiles());
		tiles.addAll(this.getRightTiles());
		tiles.add(this.getJustPickedTile());			
		
		while(tiles.size() > 0){
			Tile tile = tiles.get(0);
			List<Tile> tilesTmp = mahjong.getTileByType(tile.getType().ordinal(), tiles);
			
			for(int i = 0; i < tilesTmp.size() - 1; i++){
				Tile tileTmp = tilesTmp.get(i);
				Tile nextTileTmp = tilesTmp.get(i + 1);
				
				if(nextTileTmp.getIndex() - tileTmp.getIndex() < 3){
					this.setShiisuupuutaa(false);
					return;
				}				
			}
			
			tiles.removeAll(tilesTmp);
		}
	}
	
	/*
	 * Check CHI
	 */
	public List<List<Tile>> checkChi(Tile tile){
		List<Tile> tiles = new ArrayList<Tile>();
		tiles.addAll(this.getLeftTiles());
		tiles.add(tile);
		
		return this.checkChi(tile, tiles);
	}
	
	public List<List<Tile>> checkChi(Tile tile, List<Tile> tileSet){
		TileType type = tile.getType();
		int tileType = type.ordinal();
		int tileIndex = tile.getIndex();
		int tileId = tile.getId();
		
		List<Tile> tiles = new ArrayList<Tile>();
		tiles.addAll(tileSet);
		
		List<Tile> tileIds = null;
		List<List<Tile>> tileIdss = new ArrayList<List<Tile>>();
		
		List<Integer> tileIndexs = mahjong.getTileIndexsByType(tileType, tiles);
		
		TileComparator c = new TileComparator();
		
		if(tileIndexs.contains(tileIndex - 1) && tileIndexs.contains(tileIndex - 2)){
			tileIds = new ArrayList<Tile>();
			tileIds.add(new Tile(tileId - 1, type, tileIndex - 1));
			tileIds.add(new Tile(tileId - 2, type, tileIndex - 2));
			Collections.sort(tileIds, c);
			
			tileIdss.add(tileIds);			
		}
		if (tileIndexs.contains(tileIndex - 1) && tileIndexs.contains(tileIndex + 1)){
			tileIds = new ArrayList<Tile>();
			tileIds.add(new Tile(tileId - 1, type, tileIndex - 1));
			tileIds.add(new Tile(tileId + 1, type, tileIndex + 1));
			Collections.sort(tileIds, c);
			
			tileIdss.add(tileIds);
		}
		if (tileIndexs.contains(tileIndex + 1) && tileIndexs.contains(tileIndex + 2)){
			tileIds = new ArrayList<Tile>();
			tileIds.add(new Tile(tileId + 1, type, tileIndex + 1));
			tileIds.add(new Tile(tileId + 2, type, tileIndex + 2));
			Collections.sort(tileIds, c);
			
			tileIdss.add(tileIds);
		}
		
		return tileIdss; 
	}	
	
	public void checkTiles(Tile tile){
		
		List<Tile> tiles = new ArrayList<Tile>();
		tiles.addAll(this.getLeftTiles());		
		tiles.add(tile);
		
		TileComparator comparator = new TileComparator();		
		Collections.sort(tiles, comparator);
		
		this.checkTiles(tiles);
	}
	
	public void checkTiles(List<Tile> tileSet){
		
		List<Tile> tiles = new ArrayList<Tile>();
		tiles.addAll(tileSet);
		
		this.getKanSet().clear();
		this.getPonSet().clear();
		this.getChiSet().clear();
		this.getPairSet().clear();
		this.getDiscretedTiles().clear();
		
		while(tiles.size() > 0){
			
			Tile tileTmp = tiles.get(0);
			
			boolean isKan = this.checkKan(tileTmp, tiles);
			boolean isPon = this.checkPon(tileTmp, tiles);			
			List<List<Tile>> tileIds = this.checkChi(tileTmp, tiles);
			int tileIdsSize = tileIds.size();
			boolean isPair = this.checkPair(tileTmp, tiles);
			
			if(isKan){
				this.getKanSet().add(tileTmp);
				
				tiles.removeAll(Collections.singleton(tileTmp));				
			}
			else if(isPon){
				this.getPonSet().add(tileTmp);
			
				tiles.removeAll(Collections.singleton(tileTmp));					
			}
			else if(tileIdsSize > 0){
				
				List<Tile> tileSetTmp = tileIds.get(0);
				for(int i = 0; i < tileSetTmp.size(); i++){
					tiles.remove(tileSetTmp.get(i));
				}
				
				tiles.remove(tileTmp);	
				tileSetTmp.add(tileTmp);
				
				this.getChiSet().add(tileSetTmp);
			}
			else if(isPair){
				this.getPairSet().add(tileTmp);
				tiles.removeAll(Collections.singleton(tileTmp));				
			}
			else {
				this.getDiscretedTiles().add(tiles.remove(0));
			}					
		}
		
		TileComparator c = new TileComparator();
		
		tiles.addAll(this.getRightTiles());
		Collections.sort(tiles, c);
		
		/*
		 * TODO deadlock here
		 */
		while(tiles.size() > 0){
			
			int tileId = tiles.get(0).getId();
			int tileIndex = tiles.get(0).getIndex();
			TileType tileType = tiles.get(0).getType();
			
			Tile tileTmp = new Tile(tileId, tileType, tileIndex); 
			
			boolean isKan = this.checkKan(tileTmp, tiles);
			boolean isPon = this.checkPon(tileTmp, tiles);			
			List<List<Tile>> tileIds = this.checkChi(tileTmp, tiles);
			int tileIdsSize = tileIds.size();		
			
			if(isKan){
				this.getKanSet().add(tileTmp);				
				tiles.removeAll(Collections.singleton(tileTmp));				
			}
			else if(isPon){
				this.getPonSet().add(tileTmp);			
				tiles.removeAll(Collections.singleton(tileTmp));					
			}
			else if(tileIdsSize > 0){
				List<Tile> tileSetTmp = tileIds.get(0);
				tiles.removeAll(tileSetTmp);	
				tiles.remove(tileTmp);
				
				tileSetTmp.add(tileTmp);			
				this.getChiSet().add(tileSetTmp);
			}
			else {
				tiles.remove(0);
			}
		}
	}
	
	public void checkYakus(Tile tile){
		//YAKU		
		this.menzenTsumo();
		
		this.tenpai();
		this.riichi();
		//Ippatsu
		this.tanyao();
		this.pinfu();
		this.fanpai();
		this.rinshanKaihou();
		this.chankan();
		this.haiteiRaoyue();
		this.haiteiRaoyui();
		this.iipeikou();
		this.sanshokuShoudoukou();
		this.sanRenkou();
		//Double Riichi
		this.sanshokuDoujin();
		this.sanshokuDoukou();
		this.ittsuu();
		this.chanta();
		this.honroutou();
		this.toitoi();
		this.sanAnkou();
		this.sanKanstu();
		this.chiitoisu();
		this.shouShangen();
		this.honichi();
		this.junchan();
		this.ryanpeikou();
		this.chinitsu();
		this.nagashiMangan();
		
		//YAKUMAN
		this.suuAnkou();
		this.daiSangen();
		this.shouSuushii();
		this.daiSuushii();
		this.ryuuIisou();
		this.tsuuIisou();
		this.daiChisei();
		this.chinroutou();
		this.chuurenPoutou(tile);
		this.daiSharin(tile);
		this.suuRenkou();
		this.suuKantsu();
		
		//OTHER YAKUMAN
		//this.shiisanpuutaa();
		//this.shiisuupuutaa();
	}
	
	public boolean isSpecialComplete(){
		
		if(this.isChiiToitsu()
				|| this.isKokushiMusou13Han()
				|| this.isKokushiMusou26Han()){
								
				return true;
		}
				
		return false;
	}
	
	public boolean isNormalComplete(){
		if(((this.getKanSet().size() + this.getPonSet().size() + this.getChiSet().size()) == 4) && (this.getPairSet().size() == 1)){		
			return true;			
		}
		
		return false;
	}
	
	/*
	 * Check PAIR
	 */	
	public boolean checkPair(Tile tile){
		List<Tile> tilesSet = new ArrayList<Tile>();
		tilesSet.addAll(this.getLeftTiles());
		tilesSet.add(tile);
		
		return this.checkPair(tile, tilesSet);
	}
	
	public boolean checkPair(Tile tile, List<Tile> tiles){
		
		if(Collections.frequency(tiles, tile) == 2)
			return true;
		
		return false;
	}
	
	/*
	 * Check PON
	 */
	public boolean checkPon(Tile tile){
		List<Tile> tiles = new ArrayList<Tile>();
		tiles.addAll(this.getLeftTiles());
		tiles.add(tile);

		return this.checkPon(tile, tiles);
	}
	
	public boolean checkPon(Tile tile, List<Tile> tiles){
		if(Collections.frequency(tiles, tile) == 3)
			return true;
		
		return false;
	}
	
	/*
	 * Check OPEN KAN
	 */
	public boolean checkKan(Tile tile){
		
		List<Tile> tiles = new ArrayList<Tile>();
		tiles.addAll(this.getLeftTiles());
		tiles.add(tile);
		
		return this.checkKan(tile, tiles);
	}
	
	public boolean checkKan(Tile tile, List<Tile> tiles){
		if(Collections.frequency(tiles, tile) == 4)
			return true;
		
		return false;
	}
	
	/*
	 * Check LATE KAN
	 */
	public boolean checkLateKan(Tile tile){
		if(Collections.frequency(this.getRightTiles(), tile) == 3){
			return true;
		}
		return false;
	}
	
	/*
	 * Print the log
	 */
	public String logTileSet(){
		StringBuffer str = new StringBuffer();
		
		List<Tile> kanSet = this.getKanSet();
		List<Tile> ponSet = this.getPonSet();
		List<List<Tile>> chiSet = this.getChiSet();
		List<Tile> pairSet = this.getPairSet();
		
		str.append("\nKAN SET:" + kanSet);
		str.append("\nPONSET: " + ponSet);
		str.append("\nCHI SET:" + chiSet);
		str.append("\nPAIR SET:" + pairSet);
		
		return str.toString();
	}
	
	/*
	 * Update bonusTime
	 */
	public void updateBonusTime(){
		int bonusTimeTmp = this.getBonusTime();
		
		if(bonusTimeTmp > 0){
			long endTime = System.currentTimeMillis();
			
			this.setEndTime(endTime);
			
			long leftTime = endTime - this.getStartTime();
			if(leftTime > this.getTurnTime()){
				leftTime = leftTime - this.getTurnTime();
			}
			else {
				leftTime = 0;
			}
			
			int remainTime = this.getBonusTime() - (int)leftTime;
			
			if(remainTime > 0){
				this.setBonusTime(remainTime);
			}
			else {
				this.setBonusTime(0);
			}
		}
	}
	
	public List<Integer> getPonTileIndexs() {
		return ponTileIndexs;
	}

	public void setPonTileIndexs(List<Integer> ponTileIndexs) {
		this.ponTileIndexs = ponTileIndexs;
	}
	
	public List<List<Tile>> getWall() {
		return wall;
	}

	public void setWall(List<List<Tile>> wall) {
		this.wall = wall;
	}

	public List<Tile> getLeftTiles() {
		return leftTiles;
	}

	public void setLeftTiles(List<Tile> leftTiles) {
		this.leftTiles = leftTiles;
	}

	public List<Tile> getRightTiles() {
		return rightTiles;
	}

	public void setRightTiles(List<Tile> rightTiles) {
		this.rightTiles = rightTiles;
	}

	public List<Tile> getThrownTiles() {
		return thrownTiles;
	}

	public void setThrownTiles(List<Tile> thrownTiles) {
		this.thrownTiles = thrownTiles;
	}

	public int getPlayerPosition() {
		return playerPosition;
	}

	public void setPlayerPosition(int playerPosition) {
		this.playerPosition = playerPosition;
	}

	public boolean isDisconnect() {
		return isDisconnect;
	}

	public void setDisconnect(boolean isDisconnect) {
		this.isDisconnect = isDisconnect;
	}

	public String getPlayerState() {
		return playerState;
	}

	public void setPlayerState(String playerState) {
		this.playerState = playerState;
	}

	public boolean isContributeToDeathWall() {
		return isContributeToDeathWall;
	}

	public void setContributeToDeathWall(boolean isContributeToDeathWall) {
		this.isContributeToDeathWall = isContributeToDeathWall;
	}

	public boolean isPointing() {
		return isPointing;
	}

	public void setPointing(boolean isPointing) {
		this.isPointing = isPointing;
	}

	public boolean isJustThrown() {
		return isJustThrown;
	}

	public void setJustThrown(boolean isJustThrown) {
		this.isJustThrown = isJustThrown;
	}

	public int getNumTimesCallKan() {
		return numTimesCallKan;
	}

	public void setNumTimesCallKan(int numTimesCallKan) {
		this.numTimesCallKan = numTimesCallKan;
	}	
	
	public boolean isTempFuriten() {
		return isTempFuriten;
	}

	public void setTempFuriten(boolean isTempFuriten) {
		this.isTempFuriten = isTempFuriten;
	}

	public boolean isForeverFuriten() {
		return isForeverFuriten;
	}

	public void setForeverFuriten(boolean isForeverFuriten) {
		this.isForeverFuriten = isForeverFuriten;
	}

	public boolean isPublic() {
		return isPublic;
	}

	public void setPublic(boolean isPublic) {
		this.isPublic = isPublic;
	}

	public List<Tile> getDiscretedTiles() {
		return discretedTiles;
	}

	public void setDiscretedTiles(List<Tile> discretedTiles) {
		this.discretedTiles = discretedTiles;
	}

	public int getHanScore() {
		return hanScore;
	}

	public void setHanScore(int hanScore) {
		this.hanScore = hanScore;
	}

	public boolean isRiichiWaiting() {
		return isRiichiWaiting;
	}

	public void setRiichiWaiting(boolean isRiichiWaiting) {
		this.isRiichiWaiting = isRiichiWaiting;
	}

	public int getFuScore() {
		return fuScore;
	}

	public void setFuScore(int fuScore) {
		this.fuScore = fuScore;
	}

	public double getScore() {
		return score;
	}

	public void setScore(double score) {
		this.score = score;
	}

	public boolean isRon() {
		return isRon;
	}

	public void setRon(boolean isRon) {
		this.isRon = isRon;
	}
	
	public void setRyanmen(boolean isRyanmen){
		this.isRyanmen = isRyanmen;
	}
	
	public boolean isRyanmen(){
		return isRyanmen;
	}
	
	public void setKanchan(boolean isKanchan){
		this.isKanchan = isKanchan;
	}
	
	public boolean isKanchan(){
		return isKanchan;
	}
	
	public void setPenchan(boolean isPenchan){
		this.isPenchan = isPenchan;
	}
	
	public boolean isPenchan(){
		return isPenchan;
	}
	
	public void setShanpon(boolean isShanpon){
		this.isShanpon = isShanpon;
	}
	
	public boolean isShanpon(){
		return isShanpon;
	}
	
	public void setTanki(boolean isTanki){
		this.isTanki = isTanki;
	}
	
	public boolean isTanki(){
		return isTanki;
	}

	public boolean isTsumo() {
		return isTsumo;
	}

	public void setTsumo(boolean isTsumo) {
		this.isTsumo = isTsumo;
	}

	public double getFinalScore() {
		return finalScore;
	}

	public void setFinalScore(double finalScore) {
		this.finalScore = finalScore;
	}
	
	public void setHasPicked(boolean hasPicked){
		this.hasPicked = hasPicked;
	}
	
	public boolean hasPicked(){
		return hasPicked;
	}

	public boolean isSanshokuShoudoukou() {
		return isSanshokuShoudoukou;
	}

	public void setSanshokuShoudoukou(boolean isSanshokuShoudoukou) {
		this.isSanshokuShoudoukou = isSanshokuShoudoukou;
	}

	public boolean isIipeikou() {
		return isIipeikou;
	}

	public void setIipeikou(boolean isIipeikou) {
		this.isIipeikou = isIipeikou;
	}

	public List<Tile> getPonSet() {
		return ponSet;
	}

	public void setPonSet(List<Tile> ponSet) {
		this.ponSet = ponSet;
	}

	public List<Tile> getKanSet() {
		return kanSet;
	}

	public void setKanSet(List<Tile> kanSet) {
		this.kanSet = kanSet;
	}

	public List<List<Tile>> getChiSet() {
		return chiSet;
	}

	public void setChiSet(List<List<Tile>> chiSet) {
		this.chiSet = chiSet;
	}

	public List<Tile> getPairSet() {
		return pairSet;
	}

	public void setPairSet(List<Tile> pairSet) {
		this.pairSet = pairSet;
	}

	public boolean isSanRenkou() {
		return isSanRenkou;
	}

	public void setSanRenkou(boolean isSanRenkou) {
		this.isSanRenkou = isSanRenkou;
	}

	public boolean isDoubleRiichi() {
		return isDoubleRiichi;
	}

	public void setDoubleRiichi(boolean isDoubleRiichi) {
		this.isDoubleRiichi = isDoubleRiichi;
	}

	public boolean isSanshokuDoujin() {
		return isSanshokuDoujin;
	}

	public void setSanshokuDoujin(boolean isSanshokuDoujin) {
		this.isSanshokuDoujin = isSanshokuDoujin;
	}

	public boolean isSanshokuDoukou() {
		return isSanshokuDoukou;
	}

	public void setSanshokuDoukou(boolean isSanshokuDoukou) {
		this.isSanshokuDoukou = isSanshokuDoukou;
	}

	public boolean isIttsuu() {
		return isIttsuu;
	}

	public void setIttsuu(boolean isIttsuu) {
		this.isIttsuu = isIttsuu;
	}

	public boolean isChanta() {
		return isChanta;
	}

	public void setChanta(boolean isChanta) {
		this.isChanta = isChanta;
	}

	public boolean isHonroutou() {
		return isHonroutou;
	}

	public void setHonroutou(boolean isHonroutou) {
		this.isHonroutou = isHonroutou;
	}

	public boolean isToitoi() {
		return isToitoi;
	}

	public void setToitoi(boolean isToitoi) {
		this.isToitoi = isToitoi;
	}

	public boolean isSanAnkou() {
		return isSanAnkou;
	}

	public void setSanAnkou(boolean isSanAnkou) {
		this.isSanAnkou = isSanAnkou;
	}

	public boolean isSanKantsu() {
		return isSanKantsu;
	}

	public void setSanKantsu(boolean isSanKantsu) {
		this.isSanKantsu = isSanKantsu;
	}

	public boolean isChiiToitsu() {
		return isChiiToitsu;
	}

	public void setChiiToitsu(boolean isChiiToitsu) {
		this.isChiiToitsu = isChiiToitsu;
	}

	public boolean isShouSangen() {
		return isShouSangen;
	}

	public void setShouSangen(boolean isShouSangen) {
		this.isShouSangen = isShouSangen;
	}

	public boolean isHonichi() {
		return isHonichi;
	}

	public void setHonichi(boolean isHonichi) {
		this.isHonichi = isHonichi;
	}

	public boolean isJunchan() {
		return isJunchan;
	}

	public void setJunchan(boolean isJunchan) {
		this.isJunchan = isJunchan;
	}

	public boolean isRyanpeikou() {
		return isRyanpeikou;
	}

	public void setRyanpeikou(boolean isRyanpeikou) {
		this.isRyanpeikou = isRyanpeikou;
	}

	public boolean isChinitsu() {
		return isChinitsu;
	}

	public void setChinitsu(boolean isChinitsu) {
		this.isChinitsu = isChinitsu;
	}

	public boolean isNagashiMangan() {
		return isNagashiMangan;
	}

	public void setNagashiMangan(boolean isNagashiMangan) {
		this.isNagashiMangan = isNagashiMangan;
	}

	public boolean isInconclusive() {
		return isInconclusive;
	}

	public void setInconclusive(boolean isInconclusive) {
		this.isInconclusive = isInconclusive;
	}

	public List<Tile> getCalledChiSet() {
		return calledChiSet;
	}

	public void setCalledChiSet(List<Tile> calledChiSet) {
		this.calledChiSet = calledChiSet;
	}

	public List<Tile> getCalledKanSet() {
		return calledKanSet;
	}

	public void setCalledKanSet(List<Tile> calledKanSet) {
		this.calledKanSet = calledKanSet;
	}

	public List<Tile> getCalledPonSet() {
		return calledPonSet;
	}

	public void setCalledPonSet(List<Tile> calledPonSet) {
		this.calledPonSet = calledPonSet;
	}
	
	public int getNumYaku() {
		return numYaku;
	}

	public void setNumYaku(int numYaku) {
		this.numYaku = numYaku;
	}

	public int getNumYakuMan() {
		return numYakuMan;
	}

	public void setNumYakuMan(int numYakuMan) {
		this.numYakuMan = numYakuMan;
	}

	public boolean isKokushiMusou26Han() {
		return isKokushiMusou26Han;
	}

	public void setKokushiMusou26Han(boolean isKokushiMusou26Han) {
		this.isKokushiMusou26Han = isKokushiMusou26Han;
	}

	public boolean isKokushiMusou13Han() {
		return isKokushiMusou13Han;
	}

	public void setKokushiMusou13Han(boolean isKokushiMusou13Han) {
		this.isKokushiMusou13Han = isKokushiMusou13Han;
	}

	public Tile getJustPickedTile() {
		return justPickedTile;
	}

	public void setJustPickedTile(Tile justPickedTile) {
		this.justPickedTile = justPickedTile;
	}

	public boolean isSuuAnkou26Han() {
		return isSuuAnkou26Han;
	}

	public void setSuuAnkou26Han(boolean isSuuAnkou26Han) {
		this.isSuuAnkou26Han = isSuuAnkou26Han;
	}

	public boolean isSuuAnkou13Han() {
		return isSuuAnkou13Han;
	}

	public void setSuuAnkou13Han(boolean isSuuAnkou13Han) {
		this.isSuuAnkou13Han = isSuuAnkou13Han;
	}

	public boolean isDaiSangen13Han() {
		return isDaiSangen13Han;
	}

	public void setDaiSangen13Han(boolean isDaiSangen13Han) {
		this.isDaiSangen13Han = isDaiSangen13Han;
	}

	public boolean isShouSuushii13Han() {
		return isShouSuushii13Han;
	}

	public void setShouSuushii13Han(boolean isShouSuushii13Han) {
		this.isShouSuushii13Han = isShouSuushii13Han;
	}
	
	public boolean isDaiSuushii26Han() {
		return isDaiSuushii26Han;
	}

	public void setDaiSuushii26Han(boolean isDaiSuushii26Han) {
		this.isDaiSuushii26Han = isDaiSuushii26Han;
	}

	public boolean isRyuuIisou13Han() {
		return isRyuuIisou13Han;
	}

	public void setRyuuIisou13Han(boolean isRyuuIisou13Han) {
		this.isRyuuIisou13Han = isRyuuIisou13Han;
	}

	public boolean isTsuuIisou13Han() {
		return isTsuuIisou13Han;
	}

	public void setTsuuIisou13Han(boolean isTsuuIisou13Han) {
		this.isTsuuIisou13Han = isTsuuIisou13Han;
	}
	
	public boolean isDaiChisei26Han() {
		return isDaiChisei26Han;
	}

	public void setDaiChisei26Han(boolean isDaiChisei26Han) {
		this.isDaiChisei26Han = isDaiChisei26Han;
	}

	public boolean isChinroutou13Han() {
		return isChinroutou13Han;
	}

	public void setChinroutou13Han(boolean isChinroutou13Han) {
		this.isChinroutou13Han = isChinroutou13Han;
	}

	public boolean isChuurenPoutou13Han() {
		return isChuurenPoutou13Han;
	}

	public void setChuurenPoutou13Han(boolean isChuurenPoutou13Han) {
		this.isChuurenPoutou13Han = isChuurenPoutou13Han;
	}

	public boolean isChuurenPoutou26Han() {
		return isChuurenPoutou26Han;
	}

	public void setChuurenPoutou26Han(boolean isChuurenPoutou26Han) {
		this.isChuurenPoutou26Han = isChuurenPoutou26Han;
	}
	
	public boolean isDaiSharin13Han() {
		return isDaiSharin13Han;
	}

	public void setDaiSharin13Han(boolean isDaiSharin13Han) {
		this.isDaiSharin13Han = isDaiSharin13Han;
	}

	public boolean isSuuRenkou13Han() {
		return isSuuRenkou13Han;
	}

	public void setSuuRenkou13Han(boolean isSuuRenkou13Han) {
		this.isSuuRenkou13Han = isSuuRenkou13Han;
	}

	public boolean isSuuKantsu13Han() {
		return isSuuKantsu13Han;
	}

	public void setSuuKantsu13Han(boolean isSuuKantsu13Han) {
		this.isSuuKantsu13Han = isSuuKantsu13Han;
	}

	public boolean isIppatsu() {
		return isIppatsu;
	}

	public void setIppatsu(boolean isIppatsu) {
		this.isIppatsu = isIppatsu;
	}

	public boolean isMenzenTsumo() {
		return isMenzenTsumo;
	}

	public void setMenzenTsumo(boolean isMenzenTsumo) {
		this.isMenzenTsumo = isMenzenTsumo;
	}

	public boolean isTenpai() {
		return isTenpai;
	}

	public void setTenpai(boolean isTenpai) {
		this.isTenpai = isTenpai;
	}

	public boolean isRiichi() {
		return isRiichi;
	}

	public void setRiichi(boolean isRiichi) {
		this.isRiichi = isRiichi;
	}

	public boolean isTanyao() {
		return isTanyao;
	}

	public void setTanyao(boolean isTanyao) {
		this.isTanyao = isTanyao;
	}

	public boolean isPinfu() {
		return isPinfu;
	}

	public void setPinfu(boolean isPinfu) {
		this.isPinfu = isPinfu;
	}

	public boolean isFanpai() {
		return isFanpai;
	}

	public void setFanpai(boolean isFanpai) {
		this.isFanpai = isFanpai;
	}

	public boolean isRinshanKaihou() {
		return isRinshanKaihou;
	}

	public void setRinshanKaihou(boolean isRinshanKaihou) {
		this.isRinshanKaihou = isRinshanKaihou;
	}

	public boolean isChankan() {
		return isChankan;
	}

	public void setChankan(boolean isChankan) {
		this.isChankan = isChankan;
	}

	public boolean isHaiteiRaoyui() {
		return isHaiteiRaoyui;
	}

	public void setHaiteiRaoyui(boolean isHaiteiRaoyui) {
		this.isHaiteiRaoyui = isHaiteiRaoyui;
	}

	public boolean isHaiteiRaoyue() {
		return isHaiteiRaoyue;
	}

	public void setHaiteiRaoyue(boolean isHaiteiRaoyue) {
		this.isHaiteiRaoyue = isHaiteiRaoyue;
	}

	public boolean isShiisanpuutaa() {
		return isShiisanpuutaa;
	}

	public void setShiisanpuutaa(boolean isShiisanpuutaa) {
		this.isShiisanpuutaa = isShiisanpuutaa;
	}

	public boolean isShiisuupuutaa() {
		return isShiisuupuutaa;
	}

	public void setShiisuupuutaa(boolean isShiisuupuutaa) {
		this.isShiisuupuutaa = isShiisuupuutaa;
	}

	public boolean isKan() {
		return isKan;
	}

	public void setKan(boolean isKan) {
		this.isKan = isKan;
	}

	public boolean isPon() {
		return isPon;
	}

	public void setPon(boolean isPon) {
		this.isPon = isPon;
	}

	public boolean isChi() {
		return isChi;
	}

	public void setChi(boolean isChi) {
		this.isChi = isChi;
	}

	public boolean isLateKan() {
		return isLateKan;
	}

	public void setLateKan(boolean isLateKan) {
		this.isLateKan = isLateKan;
	}

	public Tile getJustThrownTile() {
		return justThrownTile;
	}

	public void setJustThrownTile(Tile justThrownTile) {
		this.justThrownTile = justThrownTile;
	}

	public List<String> getYakuList() {
		return yakuList;
	}

	public void setYakuList(List<String> yakuList) {
		this.yakuList = yakuList;
	}

	public List<String> getYakumanList() {
		return yakumanList;
	}

	public void setYakumanList(List<String> yakumanList) {
		this.yakumanList = yakumanList;
	}

	public List<String> getOtherYakumanList() {
		return otherYakumanList;
	}

	public void setOtherYakumanList(List<String> otherYakumanList) {
		this.otherYakumanList = otherYakumanList;
	}

	public int getStartDeathWall() {
		return startDeathWall;
	}

	public void setStartDeathWall(int startDeathWall) {
		this.startDeathWall = startDeathWall;
	}

	public int getEndDeathWall() {
		return endDeathWall;
	}

	public void setEndDeathWall(int endDeathWall) {
		this.endDeathWall = endDeathWall;
	}

	public int getStartDoras() {
		return startDoras;
	}

	public void setStartDoras(int startDoras) {
		this.startDoras = startDoras;
	}

	public int getEndDoras() {
		return endDoras;
	}

	public void setEndDoras(int endDoras) {
		this.endDoras = endDoras;
	}

	public boolean isHasThrown() {
		return hasThrown;
	}

	public void setHasThrown(boolean hasThrown) {
		this.hasThrown = hasThrown;
	}

	public List<Tile> getPickedDeathTile() {
		return pickedDeathTile;
	}

	public void setPickedDeathTile(List<Tile> pickedDeathTile) {
		this.pickedDeathTile = pickedDeathTile;
	}

	public int getNumKokushiMusou() {
		return numKokushiMusou;
	}

	public void setNumKokushiMusou(int numKokushiMusou) {
		this.numKokushiMusou = numKokushiMusou;
	}

	public List<Tile> getNonYaochuhaiTiles() {
		return nonYaochuhaiTiles;
	}

	public void setNonYaochuhaiTiles(List<Tile> nonYaochuhaiTiles) {
		this.nonYaochuhaiTiles = nonYaochuhaiTiles;
	}

	public int getTurnTime() {
		return turnTime;
	}

	public void setTurnTime(int turnTime) {
		this.turnTime = turnTime;
	}

	public int getBonusTime() {
		return bonusTime;
	}

	public void setBonusTime(int bonusTime) {
		this.bonusTime = bonusTime;
	}

	public double getExtendedScore() {
		return extendedScore;
	}

	public void setExtendedScore(double extendedScore) {
		this.extendedScore = extendedScore;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public long getStartTime() {
		return startTime;
	}

	public void setStartTime(long startTime) {
		this.startTime = startTime;
	}

	public long getEndTime() {
		return endTime;
	}

	public void setEndTime(long endTime) {
		this.endTime = endTime;
	}
	
	public Map getCache() {
		return cache;
	}

	public void setCache(Map cache) {
		this.cache = cache;
	}
	
	@Override
	public boolean equals(Object obj) {
	    if (obj == null)
	        return false;
	    
	    if (getClass() != obj.getClass())
	        return false;
	    
	    Player other = (Player)obj;	    
	    if(this.getPlayerId() == other.getPlayerId()) 
	    	return true;
	    
	    return false;
	}

	public Timer getTimer() {		
		timer = new Timer();
		return timer;
	}
	
	public void cancelTimer(){
		if(timer != null){
			timer.cancel();
			timer.purge();
		}		
	}

	public void setTimer(Timer timer) {
		this.timer = timer;
	}

	public List<TileSet> getSkipTypes() {
		return skipTypes;
	}

	public void setSkipTypes(List<TileSet> skipTypes) {
		this.skipTypes = skipTypes;
	}

	public boolean isConflict() {
		return isConflict;
	}

	public void setConflict(boolean isConflict) {
		this.isConflict = isConflict;
	}

	public List<Tile> getWillPickTiles() {
		return willPickTiles;
	}

	public void setWillPickTiles(List<Tile> willPickTiles) {
		this.willPickTiles = willPickTiles;
	}

	public Logger getLogger() {
		return logger;
	}

	public void setLogger(Logger logger) {
		this.logger = logger;
	}
}
